package cn.tpshion.vm.handler.task.support;

import cn.hutool.core.date.DateUtil;
import cn.tpshion.vm.base.config.NodeConfig;
import cn.tpshion.vm.common.constant.MsgType;
import cn.tpshion.vm.common.constant.NodeStatus;
import cn.tpshion.vm.common.utils.AESUtil;
import cn.tpshion.vm.common.utils.AssertUtil;
import cn.tpshion.vm.domain.entity.Secret;
import cn.tpshion.vm.domain.entity.table.SecretTableDef;
import cn.tpshion.vm.domain.support.MsgData;
import cn.tpshion.vm.domain.support.NodeContext;
import cn.tpshion.vm.domain.support.NodeStatusData;
import cn.tpshion.vm.domain.support.TaskData;
import cn.tpshion.vm.handler.LocalCommandHandler;
import cn.tpshion.vm.handler.SSEHandle;
import cn.tpshion.vm.mapper.SecretMapper;
import cn.tpshion.vm.mapper.TaskHisMapper;
import com.alibaba.fastjson2.JSON;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StopWatch;
import java.lang.reflect.ParameterizedType;
import java.util.Objects;

@Slf4j
public abstract class AbstractNodeHandler<T> implements NodeHandler {

    @Resource
    private SecretMapper secretMapper;

    @Resource
    protected NodeConfig nodeConfig;

    @Resource
    protected SSEHandle sseHandle;

    @Resource
    protected LocalCommandHandler localCommandHandler;

    @Resource
    protected TaskHisMapper taskHisMapper;

    @Override
    public T getData(NodeContext context) {
        if(Objects.isNull(context.getTaskData()) || Objects.isNull(context.getTaskData().getData())){
            return null;
        }
        String json = JSON.toJSONString(context.getTaskData().getData());
        ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
        return JSON.parseObject(json, type.getActualTypeArguments()[0]) ;
    }

    @Override
    public void handle(NodeContext context) throws Exception{
        TaskData taskData = context.getTaskData();
        log.info("NodeHandler taskData:{}", taskData);
        log.info("start handle nodeType:{} taskId:{}", taskData.getType(), context.getTaskId());
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        // 前置处理
        beforeHandler(context);

        // 实际节点处理
        handleNode(context);

        // 后置处理
        afterHandler(context);

        stopWatch.stop();

        String handTime = DateUtil.formatBetween(stopWatch.getTotalTimeMillis());
        log.info("end handle nodeType:{} cost:{}", taskData.getType(), handTime);
        sseHandle.sendMessage(context, MsgData.of(MsgType.INFO, String.format("处理节点：%s 耗时：%s", taskData.getTitle(), handTime)));
    }

    protected abstract void handleNode(NodeContext context) throws Exception;

    protected Secret getSecret(String type, String name){
        QueryWrapper queryWrapper = QueryWrapper.create().where(
                SecretTableDef.SECRET.TYPE.eq(type).and(SecretTableDef.SECRET.NAME.eq(name))
        );
        Secret secret = secretMapper.selectOneByQuery(queryWrapper);
        AssertUtil.isEmpty(secret, "密钥获取失败");
        secret.setSecretKey(AESUtil.decrypt(secret.getSecretKey(), nodeConfig.getSecret()));
        secret.setSecretValue(AESUtil.decrypt(secret.getSecretValue(), nodeConfig.getSecret()));
        return secret;
    }

    protected void beforeHandler(NodeContext context){
        TaskData taskData = context.getTaskData();
        taskData.setStatus(NodeStatus.RUN.getStatus());

        handleNodeStatus(context, taskData);

        sseHandle.sendMessage(context, new MsgData(MsgType.SUCCESS, "开始处理：" + taskData.getTitle()));
    }

    protected void afterHandler(NodeContext context){
        TaskData taskData = context.getTaskData();
        taskData.setStatus(NodeStatus.SUCCESS.getStatus());

        handleNodeStatus(context, taskData);

        sseHandle.sendMessage(context, new MsgData(MsgType.SUCCESS, taskData.getTitle() + " 执行成功"));
    }

    private void handleNodeStatus(NodeContext nodeContext, TaskData taskData){
        NodeStatusData status = new NodeStatusData();
        status.setId(taskData.getId());
        status.setStatus(taskData.getStatus());

        MsgData<NodeStatusData> data = new MsgData<>(MsgType.NODE);
        data.setData(status);
        sseHandle.sendMessage(nodeContext, data);
    }
}
